/* $Id: RoundTable.java 39 2009-07-24 22:48:06Z h_m_r@gmx.de $ */
package org.jugger.tournament.gui.view;

import java.awt.Color;
import java.awt.Component;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Vector;

import javax.security.auth.callback.TextInputCallback;
import javax.swing.DefaultCellEditor;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;

import org.jugger.tournament.model.Match;
import org.jugger.tournament.model.Round;
import org.jugger.tournament.model.Team;
import org.jugger.tournament.model.Tournament;

import util.I18N;
import util.MediaManager;

/**
 * @author Sven Hamer
 */
public class RoundTable extends JTable {

	private static final long serialVersionUID = 277543987002594063L;
	private Tournament tournament;

	public enum RoundColumn {
		MATCH_INFO, TEAM_A, TEAM_B, NB_POINTS_A, NB_POINTS_B, EXTRA_TIME, IS_FINISHED
	}

	public RoundTable(final Tournament tournament, Round round, boolean readonly) {

		setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		((DefaultCellEditor) getDefaultEditor(Integer.class))
				.setClickCountToStart(1);
		final JTextField field = (JTextField)((DefaultCellEditor) getDefaultEditor(Integer.class)).getComponent();
		field.addFocusListener(
			new FocusAdapter() {
		        public void focusGained(FocusEvent e) {
		            field.selectAll();
		        }
		        public void focusLost(FocusEvent e) {
		            field.select(0,0);
		        }
	    });


		if(!readonly) {
			this.addMouseListener(new MouseListener() {
				public void mouseClicked(MouseEvent arg0) {
					Point point = new Point(arg0.getX(), arg0.getY());

					int columnIndex = columnAtPoint(point);
					int rowIndex = rowAtPoint(point);

					getSelectionModel().setSelectionInterval(rowIndex, rowIndex);
					final RoundTableModel model = (RoundTableModel)getModel();

					if(!model.getReadonly()) {
						if(model.getColumnClass(columnIndex).equals(Team.class)) {

							Match match = model.getMatch(rowIndex);
							final Team teamClicked = columnIndex == RoundColumn.TEAM_A.ordinal() ? match.getTeamA() : match.getTeamB();
							final Team teamOpponent = match.getOpponent(teamClicked);
							final boolean teamAClicked = columnIndex == RoundColumn.TEAM_A.ordinal();

							if(arg0.getClickCount() > 1) {
								// edit team
								executeEditTeam(teamClicked);
							}

							// show popupmenu on right click => if on interesting cell
							if(arg0.getButton() == MouseEvent.BUTTON3) {

								if(match.isRecalculatable()) {

									// Constructor the pop-up menu
									JPopupMenu popupMenu = new JPopupMenu();

									// Create the submenu items
									Vector<Team> submenuTeams = new Vector<Team>();
									submenuTeams.add(teamClicked);
									submenuTeams.add(teamOpponent);
									for(Team team: model.getTournament().getTeamsNotPlayedInCurrentRound()) {
										if(!team.equals(teamClicked) && !team.equals(teamOpponent))
											submenuTeams.add(team);
									}

									boolean otherMatchTeams = false;
									Vector<JMenuItem> problemTeamItems = new Vector<JMenuItem>();
									for(final Team team: submenuTeams) {
										Team teamA = teamAClicked ? team : teamOpponent;
										Team teamB = teamAClicked ? teamOpponent : team;
										JMenuItem menuItem = new JMenuItem(team.toString());
										menuItem.setToolTipText(teamA + " - " + teamB);
										if(teamOpponent == team) {
											teamA = match.getTeamB();
											teamB = match.getTeamA();
											menuItem.setIcon(MediaManager.getImageIcon("images/match_switch_teams_small.png"));
											menuItem.setText(I18N.__("matchChangeTeamsSwitch"));
										}
										else if(teamClicked == team) {
											menuItem.setIcon(MediaManager.getImageIcon("images/match_switch_teams_no_change_small.png"));
											menuItem.setText(I18N.__("matchChangeTeamsNoChange"));
										}
										menuItem.addActionListener(new ActionListener() {
											public void actionPerformed(ActionEvent e) {
												model.getTournament().switchTeamsInCurrentRound(teamClicked, team);
												updateUI();
											}
										});

										if(otherMatchTeams && tournament.teamsHavePlayedAgainst(team, teamOpponent)) {
											menuItem.setIcon(MediaManager.getImageIcon("images/match_problems.png"));
											problemTeamItems.add(menuItem);
										}
										else {
											popupMenu.add(menuItem);
										}

										if(team == teamOpponent) {
											popupMenu.addSeparator();
											otherMatchTeams = true;
										}
									}

									if(submenuTeams.size() > problemTeamItems.size()+2) {
										popupMenu.addSeparator();
									}
									for(JMenuItem item: problemTeamItems) {
										popupMenu.add(item);
									}

									popupMenu.show(RoundTable.this, point.x-15, point.y-15);
									popupMenu.setSelected(popupMenu.getComponent(0));
								}
							}
						}
					}
				}
				public void mouseEntered(MouseEvent arg0) {
				}
				public void mouseExited(MouseEvent arg0) {
				}
				public void mousePressed(MouseEvent arg0) {
				}
				public void mouseReleased(MouseEvent arg0) {
				}
			});
		}

		setRound(tournament, round, readonly);
	}

	static class MatchInfoCellRenderer extends DefaultTableCellRenderer {
		private static final long serialVersionUID = 6460659344133534L;

		//	    DateFormat formatter;
	    public MatchInfoCellRenderer() {
	    	super();
   		    setHorizontalAlignment(JLabel.LEFT);
   		    setHorizontalTextPosition(SwingConstants.RIGHT);
	    }

//	    public Component getTableCellRendererComponent(JTable table,
//	    	    Object value, boolean isSelected, boolean hasFocus, int row, int column) {
//	    	return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
//	    }
	    public void setValue(Object value) {
//	        setText(value.toString());
	    	setIcon(null);
	    	setBackground(new Color(0.0f, 0.7f, 0.0f));
	    	if(value != null) {
	    		Boolean v = (Boolean)value;
	    		if(v) {
	    			// problematic match => teams already met in tournament...
			    	setIcon(MediaManager.getImageIcon("images/match_problems.png"));
	    			setBackground(Color.RED);
	    		}
	    	}
	    }
	}

	private void executeEditTeam(Team team) {
		if(Utils.showEditTeamDialog(this, team, tournament)) {
			getRootPane().repaint();
		}
	}

	public void setRound(Tournament tournament, Round round, boolean readonly) {
		this.tournament = tournament;
		setModel(new RoundTableModel(round, readonly));

		if(!readonly) {
			getColumnModel().getColumn(RoundColumn.MATCH_INFO.ordinal()).setMinWidth(22);
			getColumnModel().getColumn(RoundColumn.MATCH_INFO.ordinal()).setMaxWidth(22);
			getColumnModel().getColumn(RoundColumn.MATCH_INFO.ordinal()).setCellRenderer(new MatchInfoCellRenderer());
		}
		else {
			getColumnModel().getColumn(RoundColumn.MATCH_INFO.ordinal()).setMinWidth(0);
			getColumnModel().getColumn(RoundColumn.MATCH_INFO.ordinal()).setMaxWidth(0);
		}

		if (getColumnModel().getColumnCount() > RoundColumn.IS_FINISHED
				.ordinal()) {
			getColumnModel().getColumn(RoundColumn.IS_FINISHED.ordinal())
					.setMinWidth(70);
			getColumnModel().getColumn(RoundColumn.IS_FINISHED.ordinal())
					.setMaxWidth(70);
		}
	}

//	@Override
//	public TableCellEditor getCellEditor(int row, int col) {
//		if(col == RoundColumn.NB_POINTS_A.ordinal() || col == RoundColumn.NB_POINTS_B.ordinal()) {
//			final JTextField field = (JTextField)super.getCellEditor(row, col).getTableCellEditorComponent(this, getValueAt(row, col), true, row, col);
//			DefaultCellEditor edit = new DefaultCellEditor(field);
//		    edit.setClickCountToStart(1);
//		    field.addFocusListener(new FocusAdapter() {
//		        public void focusGained(FocusEvent e) {
//		            field.selectAll();
//		        }
//		        public void focusLost(FocusEvent e) {
//		            field.select(0,0);
//		        }
//		    });
//		    return edit;
//		}
//		else {
//			System.out.println(super.getCellEditor(row, col));
//			return super.getCellEditor(row, col);
//		}
//	}


	class RoundTableModel extends AbstractTableModel {

		private static final long serialVersionUID = -8122719073569699657L;

		private String[] columnNames = { "", "teamA", "teamB", "ptsTeamA",
				"ptsTeamB", "extraTime", "finished" };

		private Round round;
		private boolean readonly;

		public RoundTableModel(Round round, boolean readonly) {
			this.round = round;
			this.readonly = readonly;
		}

		public Match getMatch(int rowIndex) {
			return round.get(rowIndex);
		}

		public Tournament getTournament() {
			return tournament;
		}

		public boolean getReadonly() {
			return readonly;
		}

		public int getColumnCount() {
		//	return columnNames.length - (readonly ? 1 : 0);
			return columnNames.length;
		}

		public int getRowCount() {
			return round.size();
		}

		@Override
		public String getColumnName(int col) {
			return col == 0 ? "" : I18N.__(columnNames[col]);
		}

		public Object getValueAt(int row, int col) {

			Match match = round.get(row);

			switch (RoundColumn.values()[col]) {
			case MATCH_INFO:
				return readonly ? null : tournament.teamsHavePlayedAgainst(match);
			case TEAM_A:
				return match.getTeamA().getName();
			case TEAM_B:
				return match.getTeamB().getName();
			case NB_POINTS_A:
				return match.getPointsA();
			case NB_POINTS_B:
				return match.getPointsB();
			case EXTRA_TIME:
				return readonly ? I18N.__(""+match.extraTime()) : match.extraTime();
			case IS_FINISHED:
				return readonly ? I18N.__(""+match.isFinished()) : match.isFinished();
			}

			throw new RuntimeException("Invalid round table column.");
		}

		@Override
		public void setValueAt(Object value, int rowIndex, int columnIndex) {
			if(value == null)
				return;
			Match match = round.get(rowIndex);
			switch (RoundColumn.values()[columnIndex]) {
			case NB_POINTS_A:
				match.setPointsA((Integer) value);
				fireTableCellUpdated(rowIndex, columnIndex);
				break;
			case NB_POINTS_B:
				match.setPointsB((Integer) value);
				fireTableCellUpdated(rowIndex, columnIndex);
				break;
			case EXTRA_TIME:
				match.setExtraTime(((Boolean) value).booleanValue());
				fireTableCellUpdated(rowIndex, columnIndex);
				break;
			case IS_FINISHED:
				if (match.isFinishable()) {
					match.setFinished(((Boolean) value).booleanValue());
					fireTableCellUpdated(rowIndex, columnIndex);
				}
				break;
			}
		}

		@Override
		public Class<? extends Object> getColumnClass(int c) {
			if(RoundColumn.values()[c] == RoundColumn.TEAM_A || RoundColumn.values()[c] == RoundColumn.TEAM_B)
				return Team.class;
			return getValueAt(0, c).getClass();
		}

		@Override
		public boolean isCellEditable(int rowIndex, int columnIndex) {
			if(readonly)
				return false;

			Match match = round.get(rowIndex);

//			if (
//					(columnIndex == RoundColumn.TEAM_A.ordinal() || columnIndex == RoundColumn.TEAM_B.ordinal())
//					&& match.isRecalculatable()
//				)
//				return true;

			if (columnIndex == RoundColumn.IS_FINISHED.ordinal()
					&& match.isFinishable())
				return true;
			if (!match.isFinished()) {
				if (columnIndex == RoundColumn.NB_POINTS_A.ordinal()
						|| columnIndex == RoundColumn.NB_POINTS_B.ordinal()
						|| columnIndex == RoundColumn.EXTRA_TIME.ordinal())
					return true;
			}
			return false;
		}
	}
}
